/*
 * imageEditor 1.0 - Plugin for jQuery
 * 
 * Dual licensed under the MIT (http://www.opensource.org/licenses/mit-license.php)
 * and GPL (http://www.opensource.org/licenses/gpl-license.php) licenses.
 *
 * Depends:
 *   jquery.js
 *	 jquery.xWindow.js
 *	 templating.js
 *	 ui.base.js
 * 	 ui.draggable.js
 *   ui.resizable.js
 *	 ui.slider.js
 * 
 *
 *  Copyright (c) 2008 Oleg Slobodskoi (ajaxsoft.de)
 */


;(function($){


	$.fn.extend({
		imageEditor: function( options ) {
		
			var	args = Array.prototype.slice.call(arguments, 1);

			return this.each(function() {
				var method = (typeof options == 'string') ? options : 'init',
					imageEditor = $.data(this, 'imageEditor') || $.data( this, 'imageEditor', new $.imageEditor(this, options));
				imageEditor[method].apply(imageEditor, args);
			});
	

		}
	});

	$.imageEditor = function(elem, options)
	{

		/**************************************************************************/
		//Defaults (private)

		var d = {
			root: '/',
			path: 'fotos',
			fileName: '/IMG_1287.jpg',
			aspectRatio: 3/4,
			draggableOptions: {
				scroll: true,
				scrollSensitivity: 100
			},
			resizableOptions: {
				minWidth: 30,
				minHeight: 30
			},
			sliderOptions: {
				min: 1,
				max: 100
			},
			speed: 300,
			xWindowDefaults: null,
			rotate: function(){},
			crop: function(){},
			save: function(){}
			
		};
		
		var xWindowDefaults = {
			title: 'Image Editor',
			width: 660,
			height: 500,
			resize: resizeContent,
			buttons: {
				ok: {
					title: '&Auml;nderungen &uuml;bernehmen und speichern',
					onclick: function(){
						createCallback(d.save, [src, d.path]);
					}
				}
			}
		};

		$.extend(d, options);
		$.extend(xWindowDefaults, options.xWindowOptions);		
		
		/**************************************************************************/
		//Privat variables

		var 
			$elem = $(elem),
			$container,
			$bgImage,
			$image, 
			$overlay,
			$cropperContainer,
			$slider,
			$toolbar,
			self = this,
			imageCSS = {},
			cacheImageCSS = {},
			cropperCSS = {
				width: 0,
				height: 0,
				left: 0,
				top: 0
			},
			clipCSS = {},
			containerSize, 
			imageSize,
			ratio,
			cropperEnabled = false,
			toolbarHeight,
			src = d.root+d.path+d.fileName
		;
	
		/**************************************************************************/
		//Public variables

		this.$elem = $elem;
		
		/**************************************************************************/
		//Public methods
		this.init = function()
		{
			create ();
		};

		this.defaults = function (opt, callback)
		{
			opt ? $.extend(d, opt) : callback && callback(d);
		};
		
		this.update = function (str)
		{
			src = str || src;
			$toolbar.find('.disabled').removeClass('disabled');
			$container.empty();
			$cropperContainer = null;
			$elem.addClass('loading');
			preloadImage(src,initImage);
			
		};
		
		this.destroy = function ()
		{
			$elem.xWindow('destroy');
		};
		

		/**************************************************************************/
		//Private methods
		function create ()
		{
			$elem.appendTo('body').addClass('image-editor loading').click(clickHandler).
			change(function(e){
				e.target.name == 'ratio' && aspectRatio(e.target.value, e);	
			}).
			get(0).innerHTML = template('imageEditorTemplate',{});
			$slider = $('#image-editor-slider');
			$toolbar = $('#image-editor-toolbar');

			toolbarHeight = $toolbar.height();
			
			
			$elem.xWindow(xWindowDefaults);

			$container = $('<div class="container"/>').css(containerSize).appendTo($elem);
			preloadImage(src, initImage);

		};
		
		function initImage ()
		{
			$elem.removeClass('loading');
			imageSize = {
				width: this.width,
				height: this.height
			};

			ratio = this.width/this.height;

			$bgImage = $(this).addClass('background').
			appendTo($container);

			$overlay = $('<div class="overlay" rel="centerCropper"/>').
			appendTo($container);
			
			$image = $('<img src="'+this.src+'" class="image" />').
			appendTo($container);
				
			if (containerSize.width>=containerSize.height)
			{
				imageCSS.height = containerSize.height;
				imageCSS.width = Math.round( containerSize.height*ratio );
			} else
			{
				imageCSS.height = containerSize.height/ratio;
				imageCSS.width = containerSize.width;
			};
			
			centerImage();	
			cropper();	

			$.ui.slider && slider();
			$.fn.draggable && draggable();
			$.fn.resizable && resizable();
		};
		
		function centerImage ()
		{
			
			imageCSS.left = Math.round( (containerSize.width-imageCSS.width)/2 );
			imageCSS.top = Math.round( (containerSize.height-imageCSS.height)/2 );
			imageCSS.left = imageCSS.left<0 ? 0 : imageCSS.left;
			imageCSS.top = imageCSS.top<0 ? 0 : imageCSS.top;

			$bgImage.css(imageCSS);
			$overlay.css(imageCSS);
			$image.css(imageCSS);
			
			cropper();
		};

		function cropper (css)
		{
			if ( !$cropperContainer)
				$cropperContainer = $('<div class="cropper"><div/></div>').appendTo($container);

			if (!css) css= {};

			var diff = (cacheImageCSS.width-imageCSS.width)/cacheImageCSS.width || 0;
			cropperCSS.width = css.width || Math.round(cropperCSS.width - cropperCSS.width*diff);
			
			diff = (cacheImageCSS.height-imageCSS.height)/cacheImageCSS.height || 0;
			cropperCSS.height = css.height || Math.round(cropperCSS.height - cropperCSS.height*diff);
			
			diff =  (cropperCSS.top-cacheImageCSS.top)/cacheImageCSS.height;
			cropperCSS.top = css.top || Math.round(imageCSS.height*diff+imageCSS.top) || 0;
			
			diff = (cropperCSS.left-cacheImageCSS.left)/cacheImageCSS.width;
			cropperCSS.left = css.left || Math.round(imageCSS.width*diff + imageCSS.left) || 0;

			$cropperContainer.css(cropperCSS);
			
			$.extend(cacheImageCSS,imageCSS);

			return true;
		};

		function clip (sw)
		{
			clipCSS = {
				top: cropperCSS.top+1-imageCSS.top,
				right: cropperCSS.left-imageCSS.left + cropperCSS.width +1,
				down: cropperCSS.top + cropperCSS.height -imageCSS.top+1,
				left: cropperCSS.left-imageCSS.left+1
			};
			
			$image.css('clip', (sw != 'hide') ? 'rect('+clipCSS.top+'px, '+clipCSS.right+'px, '+clipCSS.down+'px, '+clipCSS.left+'px)' : 'rect(auto auto auto auto)');
		};
		
		function resizeImage ()
		{
			$bgImage.css(imageCSS);
			$overlay.css(imageCSS);
			$image.css(imageCSS);
			cropperEnabled && cropper() && clip();
			
		};

		function toggleCropper (e)
		{
			$(e.target).toggleClass('disabled');
			var action = $(e.target).hasClass('disabled') ? 'show' : 'hide';
			
			centerCropper();
			clip(action);

			$bgImage[action]();
			$overlay[action]();
			$cropperContainer[action]();
			cropperEnabled = action == 'show' ? true : false;
		};	
		
		function centerCropper ()
		{

			var width = (containerSize.width>imageCSS.width) ? imageCSS.width*30/100 : containerSize.width*30/100;
			cropperCSS.width = Math.round( width );
			cropperCSS.height = Math.round( !d.aspectRatio || parseFloat(d.aspectRatio) == 'NaN'? width : width*d.aspectRatio);
			cropperCSS.left = Math.round( ( (containerSize.width>imageCSS.width ? imageCSS.width: containerSize.width) - cropperCSS.width  )/2 + imageCSS.left + $container.scrollLeft());
			cropperCSS.top = Math.round( (	(containerSize.height>imageCSS.height ? imageCSS.height: containerSize.height) - cropperCSS.height)/2 + imageCSS.top + $container.scrollTop() );
			cropper(cropperCSS);
			clip();
		};
		

		function aspectRatio (ratio, e)
		{	
			d.aspectRatio = parseFloat(ratio);
			$cropperContainer.resizable('destroy');
			resizable();
		};
		
		function draggable ()
		{
			$.extend(d.draggableOptions, {
				containment: $container,
				drag: function(e, ui)
				{	
					cropperCSS.top = ui.position.top;
					cropperCSS.left = ui.position.left;
					clip();
				}
			
			});
			$cropperContainer.draggable(d.draggableOptions);
		
		};
		
		function resizable ()
		{
			$.extend(d.resizableOptions, {
				aspectRatio: d.aspectRatio,
				containment: $container,
				handles: {
					nw: $('<span class="ui-resizable-handle ui-resizable-nw"/>').appendTo($cropperContainer),
					ne:  $('<span class="ui-resizable-handle ui-resizable-ne"/>').appendTo($cropperContainer),
					sw: $('<span class="ui-resizable-handle ui-resizable-sw"/>').appendTo($cropperContainer),
					se: $('<span class="ui-resizable-handle ui-resizable-se"/>').appendTo($cropperContainer)
				},
				resize: function(e, ui){
					cropperCSS = {
						width: Math.round(ui.size.width),
						height: Math.round(ui.size.height),
						top: Math.round(ui.position.top),
						left: Math.round(ui.position.left)
					};
					clip();
				},
				stop: function(e, ui){
					//define real values after stop becouse of shifting
					cropperCSS = {
						width: Math.round( ui.element.width() ),
						height: Math.round( ui.element.height() ),
						top: Math.round( ui.element.position().top + $container.scrollTop()),
						left: Math.round( ui.element.position().left + $container.scrollLeft() )
					};

					clip();
				}
			});

			$cropperContainer.resizable(d.resizableOptions);		
		};
		
		function slider ()
		{
			$.extend(d.sliderOptions, {
				slide: function (e, ui)
				{
					zoom(ui.value);
				},
				startValue: imageCSS.width*100/imageSize.width
			
			});
			$slider.slider(d.sliderOptions);		
		};
		
		function zoom (value, e)
		{
			if (typeof value != 'string')
			{
				imageCSS.width = Math.round( imageSize.width*value/100 );
				imageCSS.height = Math.round( imageSize.height*value/100 );
				imageCSS.top = Math.round( (containerSize.height-imageCSS.height)/2 );
				imageCSS.left = Math.round( (containerSize.width-imageCSS.width)/2 );
				
				if (imageCSS.top<0) imageCSS.top = 0;
				if (imageCSS.left<0) imageCSS.left = 0;
				resizeImage();
			} else
				$slider.slider('moveTo',value);
		};
		
		
		function clickHandler (e)
		{
			var data = $(e.target).attr('rel');
			if (!data ) return;
			data = data.split(':');
			var f = data[0];
			data.shift();
			data.push(e);
			createCallback(f, data);
		};
		
		function createCallback (fn,params)
		{	
			typeof fn == 'string' && eval('fn='+fn);
			typeof fn == 'function' && fn.apply(self,params);
		};	

		function preloadImage(src, callback)
		{
			var img= new Image();
			img.onload=callback;
			img.src= src+'?no-cache='+Math.random();
		};
		
		function resizeContent (xWindow)
		{	
			containerSize = {
				height: xWindow.contentSize.height-toolbarHeight,
				width: xWindow.contentSize.width
			};

			$container && $container.css(containerSize) && centerImage();

		};
		
		/**************************************************************************/
		//Internal callbacks
		function rotate (direction, e)
		{
			createCallback(d.rotate,[src, direction]);
		};
		
		function crop ()
		{
			if (!cropperEnabled) return;
			var top = Math.round(imageSize.height*clipCSS.top/imageCSS.height),
				left = Math.round(imageSize.width*clipCSS.left/imageCSS.width);
	
			var region = {
				left: left<0 ? 0 : left,
				top: top<0 ? 0: top,
				width: Math.round(imageSize.width*(clipCSS.right-clipCSS.left)/imageCSS.width),
				height: Math.round(imageSize.height*(clipCSS.down-clipCSS.top)/imageCSS.height)
			};
			
			
			createCallback(d.crop, [src, region]);
		};
		
		function undo ()
		{
			self.update(d.src);
		};



		
		
		
		
	};



})(jQuery);		